package com.aivin.tvslidebar.scrool2;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
import android.view.animation.Interpolator;
import android.widget.Scroller;

import com.aivin.tvslidebar.R;
import com.aivin.tvslidebar.activity.LyLog;

import java.util.ArrayList;
import java.util.List;


/**
 * 滚动选择器
 */
public abstract class ScrollPickerView extends View {
    /**
     * 可见的item数量
     */
    private int mVisibleItemCount = 7;
    /**
     * 快速滑动时是否惯性滚动一段距离，默认开启
     */
    private boolean mIsInertiaScroll = true;
    /**
     * 是否循环滚动，默认开启
     */
    private boolean mIsCirculation = true;
    /**
     * 设置为true为不允许拦截，此时该设置才生效
     * 当嵌入到ScrollView等滚动组件中，为了使该自定义滚动选择器可以正常工作，请设置为true
     */
    private boolean mDisallowInterceptTouch = true;

    /**
     * 当前选中的item下标
     */
    private int mSelected;
    private List<String> mData;
    /**
     * 每个条目的高度
     */
    private int mItemHeight = 0;
    /**
     * 每个条目的宽度，当水平滚动时，宽度=mMeasureWidth／mVisibleItemCount
     */
    private int mItemWidth = 0;
    /**
     * 水平滚动时，mItemSize = mItemWidth
     */
    private int mItemSize;
    /**
     * 中间item的位置，0<=mCenterPosition＜mVisibleItemCount，默认为 mVisibleItemCount / 2
     */
    private int mCenterPosition = -1;
    /**
     * 中间item的起始坐标x(不考虑偏移)
     */
    private int mCenterX;
    /**
     * 水平滚动时，mCenterPoint = mCenterX
     */
    private int mCenterPoint;
    /**
     * 触摸的坐标X
     */
    private float mLastMoveX;

    /**
     * item移动长度，负数表示向左移动，正数表示向右移动
     */
    private float mMoveLength = 0;

    private GestureDetector mGestureDetector;
    private OnSelectedListener mListener;
    private final Scroller mScroller;

    /**
     * 是否正在惯性滑动
     */
    private boolean mIsFling;
    /**
     * 是否正在滑向中间
     */
    private boolean mIsMovingCenter;
    /**
     * 可以把scroller看做模拟的触屏滑动操作，mLastScrollX为上次触屏滑动的x坐标
     */
    private int mLastScrollX = 0;

    private boolean mDisallowTouch = false; // 不允许触摸

    private final Paint mPaint;
    /**
     * 单击切换选项或触发点击监听器
     */
    private boolean mCanTap = true;
    /**
     * 是否绘制每个item(包括在边界外的item)
     */
    private final boolean mDrawAllItem = false;
    /**
     * 用于标志第一次设置selected时把事件通知给监听器
     */
    private boolean mHasCallSelectedListener = false;
    private int mSelectedOnTouch;
    private boolean mIsAutoScrolling = false;
    private final ValueAnimator mAutoScrollAnimator;
    private final SlotInterpolator sAutoScrollInterpolator = new SlotInterpolator();

    public ScrollPickerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScrollPickerView(Context context, AttributeSet attrs,
                            int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mGestureDetector = new GestureDetector(getContext(), new FlingOnGestureListener());
        mScroller = new Scroller(getContext());
        mAutoScrollAnimator = ValueAnimator.ofInt(0, 0);

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.FILL);

        init(attrs);
    }

    private void init(AttributeSet attrs) {
        if (attrs != null) {
            TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ScrollPickerView);
            setVisibleItemCount(typedArray.getInt(R.styleable.ScrollPickerView_spv_visible_item_count, mVisibleItemCount));
            setCenterPosition(typedArray.getInt(R.styleable.ScrollPickerView_spv_center_item_position, mCenterPosition));
            setHorizontal();
            typedArray.recycle();
        }
    }


    @Override
    protected void onDraw(Canvas canvas) {
        if (mData == null || mData.size() <= 0) {
            return;
        }


        // 只绘制可见的item
        int length = Math.max(mCenterPosition + 1, mVisibleItemCount - mCenterPosition);
        int position;
        int start = Math.min(length, mData.size());
        if (mDrawAllItem) {
            start = mData.size();
        }
        // 上下两边
        for (int i = start; i >= 1; i--) { // 先从远离中间位置的item绘制，当item内容偏大时，较近的item覆盖在较远的上面

            if (mDrawAllItem || i <= mCenterPosition + 1) {  // 上面的items,相对位置为 -i
                position = mSelected - i < 0 ? mData.size() + mSelected - i
                        : mSelected - i;
                // 传入位置信息，绘制item
                if (mIsCirculation) {
                    drawItem(canvas, mData, position, -i, mMoveLength, mCenterPoint + mMoveLength - i * mItemSize);
                } else if (mSelected - i >= 0) { // 非循环滚动
                    drawItem(canvas, mData, position, -i, mMoveLength, mCenterPoint + mMoveLength - i * mItemSize);
                }
            }
            if (mDrawAllItem || i <= mVisibleItemCount - mCenterPosition) {  // 下面的items,相对位置为 i
                position = mSelected + i >= mData.size() ? mSelected + i
                        - mData.size() : mSelected + i;
                // 传入位置信息，绘制item
                if (mIsCirculation) {
                    drawItem(canvas, mData, position, i, mMoveLength, mCenterPoint + mMoveLength + i * mItemSize);
                } else if (mSelected + i < mData.size()) { // 非循环滚动
                    drawItem(canvas, mData, position, i, mMoveLength, mCenterPoint + mMoveLength + i * mItemSize);
                }
            }
        }
        // 选中的item
        drawItem(canvas, mData, mSelected, 0, mMoveLength, mCenterPoint + mMoveLength);
    }

    /**
     * 绘制item
     *
     * @param relative   相对中间item的位置,relative==0表示中间item,relative<0表示左边的item,relative>0表示右边的item
     * @param moveLength 中间item滚动的距离，moveLength<0 则表示向右滚动的距离， moveLength＞0则表示向左滚动的距离
     * @param leftX      当前绘制item的坐标, 当水平滚动时为item最左边x的坐标
     */
    public abstract void drawItem(Canvas canvas, List<String> data, int position, int relative, float moveLength, float leftX);

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        reset();
    }

    private void reset() {
        if (mCenterPosition < 0) {
            mCenterPosition = mVisibleItemCount / 2;
        }

        mItemHeight = getMeasuredHeight();
        mItemWidth = getMeasuredWidth() / mVisibleItemCount;

        mCenterX = mCenterPosition * mItemWidth;

        mItemSize = mItemWidth;
        mCenterPoint = mCenterX;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mDisallowTouch) {
            return true;
        }

        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
            mSelectedOnTouch = mSelected;
        }

        if (mGestureDetector.onTouchEvent(event)) {
            return true;
        }

        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_MOVE:
                if (Math.abs(event.getX() - mLastMoveX) < 0.1f) {
                    return true;
                }
                mMoveLength += event.getX() - mLastMoveX;

                mLastMoveX = event.getX();
                checkCirculation();
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                mLastMoveX = event.getX();
                if (mMoveLength == 0) {
                    if (mSelectedOnTouch != mSelected) { //前后发生变化
                        notifySelected();
                    }
                } else {
                    moveToCenter(); // 滚动到中间位置
                }
                break;
        }
        return true;
    }


    private void computeScroll(int curr, int end, float rate) {
        if (rate < 1) { // 正在滚动
            // 可以把scroller看做模拟的触屏滑动操作，mLastScrollX为上次滑动的坐标
            mMoveLength = mMoveLength + curr - mLastScrollX;
            mLastScrollX = curr;

            checkCirculation();
            invalidate();
        } else { // 滚动完毕
            mIsMovingCenter = false;
            mLastScrollX = 0;
            // 直接居中，不通过动画
            if (mMoveLength > 0) { //// 向下滑动
                if (mMoveLength < mItemSize * 1f / 2) {
                    mMoveLength = 0;
                } else {
                    mMoveLength = mItemSize;
                }
            } else {
                if (-mMoveLength < mItemSize * 1f / 2) {
                    mMoveLength = 0;
                } else {
                    mMoveLength = -mItemSize;
                }
            }
            checkCirculation();
            notifySelected();
            invalidate();
        }

    }

    @Override
    public void computeScroll() {
        // 正在滚动
        if (mScroller.computeScrollOffset()) {
            // 可以把scroller看做模拟的触屏滑动操作，mLastScrollX为上次滑动的坐标
            mMoveLength = mMoveLength + mScroller.getCurrX() - mLastScrollX;
            mLastScrollX = mScroller.getCurrX();
            checkCirculation(); //　检测当前选中的item
            invalidate();
        } else { // 滚动完毕
            if (mIsFling) {
                mIsFling = false;
                if (mMoveLength == 0) { //惯性滑动后的位置刚好居中的情况
                    notifySelected();
                } else {
                    moveToCenter(); // 滚动到中间位置
                }
            } else if (mIsMovingCenter) { // 选择完成，回调给监听器
                notifySelected();
            }
        }
    }

    public void cancelScroll() {
        mLastScrollX = 0;
        mIsFling = mIsMovingCenter = false;
        mScroller.abortAnimation();
        stopAutoScroll();
    }

    // 检测当前选择的item位置
    private void checkCirculation() {
        if (mMoveLength >= mItemSize) { // 向下滑动
            // 该次滚动距离中越过的item数量
            int span = (int) (mMoveLength / mItemSize);
            mSelected -= span;
            if (mSelected < 0) {  // 滚动顶部，判断是否循环滚动
                if (mIsCirculation) {
                    do {
                        mSelected = mData.size() + mSelected;
                    } while (mSelected < 0); // 当越过的item数量超过一圈时
                    mMoveLength = (mMoveLength - mItemSize) % mItemSize;
                } else { // 非循环滚动
                    mSelected = 0;
                    mMoveLength = mItemSize;
                    if (mIsFling) { // 停止惯性滑动，根据computeScroll()中的逻辑，下一步将调用moveToCenter()
                        mScroller.forceFinished(true);
                    }
                    if (mIsMovingCenter) { //  移回中间位置
                        scroll(mMoveLength, 0);
                    }
                }
            } else {
                mMoveLength = (mMoveLength - mItemSize) % mItemSize;
            }

        } else if (mMoveLength <= -mItemSize) { // 向上滑动
            // 该次滚动距离中越过的item数量
            int span = (int) (-mMoveLength / mItemSize);
            mSelected += span;
            if (mSelected >= mData.size()) { // 滚动末尾，判断是否循环滚动
                if (mIsCirculation) {
                    do {
                        mSelected = mSelected - mData.size();
                    } while (mSelected >= mData.size()); // 当越过的item数量超过一圈时
                    mMoveLength = (mMoveLength + mItemSize) % mItemSize;
                } else { // 非循环滚动
                    mSelected = mData.size() - 1;
                    mMoveLength = -mItemSize;
                    if (mIsFling) { // 停止惯性滑动，根据computeScroll()中的逻辑，下一步将调用moveToCenter()
                        mScroller.forceFinished(true);
                    }
                    if (mIsMovingCenter) { //  移回中间位置
                        scroll(mMoveLength, 0);
                    }
                }
            } else {
                mMoveLength = (mMoveLength + mItemSize) % mItemSize;
            }
        }
    }

    /**
     * 移动到中间位置
     */
    private void moveToCenter() {
        if (!mScroller.isFinished() || mIsFling || mMoveLength == 0) {
            return;
        }

        cancelScroll();

        // 向右滑动
        if (mMoveLength > 0) {
            if (mMoveLength < mItemWidth / 2) {
                scroll(mMoveLength, 0);
            } else {
                scroll(mMoveLength, mItemWidth);
            }

        } else {
            if (-mMoveLength < mItemWidth / 2) {
                scroll(mMoveLength, 0);
            } else {
                scroll(mMoveLength, -mItemWidth);
            }
        }
    }

    private void scroll(float from, int to) {
        mLastScrollX = (int) from;
        mIsMovingCenter = true;
        mScroller.startScroll((int) from, 0, 0, 0);
        mScroller.setFinalX(to);
        invalidate();
    }

    /**
     * 惯性滑动
     */
    private void fling(float from, float vel) {
        mLastScrollX = (int) from;
        mIsFling = true;
        // 最多可以惯性滑动10个item
        mScroller.fling((int) from, 0, (int) vel, 0, -10 * mItemWidth,
                10 * mItemWidth, 0, 0);
        invalidate();
    }

    private void notifySelected() {
        mMoveLength = 0;
        cancelScroll();
        if (mListener != null) {
            mListener.onSelected(ScrollPickerView.this, mSelected, getSelectedItem());
        }
    }



    /**
     * @param endY         　需要滚动到的位置
     * @param duration     　滚动时间
     * @param canIntercept 能否终止滚动，比如触摸屏幕终止滚动
     */
    public void autoScrollTo(final int endY, long duration, final Interpolator interpolator, boolean canIntercept) {
        if (mIsAutoScrolling) {
            return;
        }
        final boolean temp = mDisallowTouch;
        mDisallowTouch = !canIntercept;
        mIsAutoScrolling = true;
        mAutoScrollAnimator.cancel();
        mAutoScrollAnimator.setIntValues(0, endY);
        mAutoScrollAnimator.setInterpolator(interpolator);
        mAutoScrollAnimator.setDuration(duration);
        mAutoScrollAnimator.removeAllUpdateListeners();
        mAutoScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float rate = animation.getCurrentPlayTime() * 1f / animation.getDuration();
                computeScroll((int) animation.getAnimatedValue(), endY, rate);
            }
        });
        mAutoScrollAnimator.removeAllListeners();
        mAutoScrollAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                mIsAutoScrolling = false;
                mDisallowTouch = temp;
            }
        });
        mAutoScrollAnimator.start();
    }


    /**
     * 停止自动滚动
     */
    public void stopAutoScroll() {
        mIsAutoScrolling = false;
        mAutoScrollAnimator.cancel();
    }

    private static class SlotInterpolator implements Interpolator {
        @Override
        public float getInterpolation(float input) {
            return (float) (Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
        }
    }


    /**
     * 快速滑动时，惯性滑动一段距离
     */
    private class FlingOnGestureListener extends SimpleOnGestureListener {

        private boolean mIsScrollingLastTime = false;

        @Override
        public boolean onDown(MotionEvent e) {
            if (mDisallowInterceptTouch) {
                ViewParent parent = getParent();
                if (parent != null) {
                    // 不允许父组件拦截事件
                    parent.requestDisallowInterceptTouchEvent(true);
                }
            }
            // 记录是否从滚动状态终止
            mIsScrollingLastTime = isScrolling();
            // 点击时取消所有滚动效果
            cancelScroll();
            mLastMoveX = e.getX();
            return true;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, final float velocityY) {
            // 惯性滑动
            if (mIsInertiaScroll) {
                cancelScroll();
                fling(mMoveLength, velocityX);
            }
            return true;
        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            mLastMoveX = e.getX();
            mCenterPoint = mCenterX;

            float lastMove = mLastMoveX;

            if (mCanTap && !mIsScrollingLastTime) {
                if (lastMove >= mCenterPoint && lastMove <= mCenterPoint + mItemSize) {
                    //点击中间item，回调点击事件
                    LyLog.showLog("点击===" + mSelected);
                    performClick();
                } else if (lastMove < mCenterPoint) {
                    // 点击两边的item，移动到相应的item
                    //int move = mItemSize;
                    int move = (int) (e.getX() - mCenterX);
                    autoScrollTo(-move + mItemSize, 150, sAutoScrollInterpolator, false);
                } else {
                    // lastMove > mCenterPoint + mItemSize
                    //int move = -mItemSize;
                    int move = (int) (e.getX() - mCenterX);
                    autoScrollTo(-move, 150, sAutoScrollInterpolator, false);
                }
            } else {
                moveToCenter();
            }
            return true;
        }
    }

    public void setOnSelectedListener(OnSelectedListener listener) {
        mListener = listener;
    }

    public void setData(List<String> data) {
        if (data == null) {
            mData = new ArrayList<>();
        } else {
            this.mData = data;
        }
        mSelected = mData.size() / 2;
        invalidate();
    }


    public String getSelectedItem() {
        return mData.get(mSelected);
    }


    public void setSelectedPosition(int position) {
        if (position < 0 || position > mData.size() - 1
                || (position == mSelected && mHasCallSelectedListener)) {
            return;
        }

        mHasCallSelectedListener = true;
        mSelected = position;
        invalidate();
        notifySelected();
    }


    public void setVisibleItemCount(int visibleItemCount) {
        mVisibleItemCount = visibleItemCount;
        reset();
        invalidate();
    }


    public int getItemHeight() {
        return mItemHeight;
    }

    public int getItemWidth() {
        return mItemWidth;
    }

    /**
     * 水平滚动时，mItemSize = mItemWidth
     */
    public int getItemSize() {
        return mItemSize;
    }


    /**
     * 中间item的位置，0 <= centerPosition <= mVisibleItemCount
     */
    public void setCenterPosition(int centerPosition) {
        if (centerPosition < 0) {
            mCenterPosition = 0;
        } else if (centerPosition >= mVisibleItemCount) {
            mCenterPosition = mVisibleItemCount - 1;
        } else {
            mCenterPosition = centerPosition;
        }
        invalidate();
    }


    public boolean isScrolling() {
        return mIsFling || mIsMovingCenter || mIsAutoScrolling;
    }


    public void setHorizontal() {
        reset();
        mItemSize = mItemWidth;
        invalidate();
    }


    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
        if (visibility == VISIBLE) {
            moveToCenter();
        }
    }

}